package com.fqr;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateParser {

	private static final DateFormat DF = new SimpleDateFormat("MM/dd/yyyy");

	static {
		DF.setLenient(false);
	}

	public static Date parse(String s) throws ParseException {
		return parse(s, false);
	}

	public static Date parse(String s, boolean eur) throws ParseException {
		try {
			if (s == null || s.trim().length() == 0
					|| s.equalsIgnoreCase("CALC")) {
				return null;
			}

			if (s.indexOf('\n') > 0)
				s = s.substring(0, s.indexOf('\n'));

			if (s.indexOf('\r') > 0)
				s = s.substring(0, s.indexOf('\r'));

			int year = 0;
			int month = 0;
			int day = 0;

			String[] fields = null;
			if (s.indexOf('/') > 0) {
				if (s.indexOf(' ') > 0)
					s = s.substring(0, s.indexOf(' '));

				fields = s.split("/");
				if (eur) {
					month = Integer.parseInt(fields[1]);
					day = Integer.parseInt(fields[0]);
				} else {
					month = Integer.parseInt(fields[0]);
					day = Integer.parseInt(fields[1]);
				}
				year = Integer.parseInt(fields[2]);
			} else if (s.indexOf('-') > 0 || s.indexOf(' ') > 0
					|| s.indexOf(',') > 0) {
				s = s.replace(' ', '-');
				s = s.replace(',', '-');

				fields = s.split("-");
				if (fields[0].trim().length() >= 3
						&& !Character.isDigit(fields[0].charAt(0))) {
					// MMM DD YYYY
					int i = 0;
					for (String field : fields) {
						if (field.trim().length() > 0) {
							if (i == 0)
								month = parseMonth(field);
							else if (i == 1)
								day = Integer.parseInt(field);
							else if (i == 2)
								year = Integer.parseInt(field);
							i++;
						}
					}
				} else {
					int first = Integer.parseInt(fields[0]);
					int last = Integer.parseInt(fields[2]);

					if (first > 31) {
						// YYYY-MM-DD or YYYY-MMM-DD
						year = first;
						day = last;
						if (fields[1].trim().length() >= 3) {
							month = parseMonth(fields[1]);
						} else {
							month = Integer.parseInt(fields[1]);
						}
					} else {
						// MM-DD-YYYY or DD-MMM-YYYY
						year = last;
						if (fields[1].trim().length() >= 3) {
							month = parseMonth(fields[1]);
							day = first;
						} else {
							month = first;
							day = Integer.parseInt(fields[1]);
						}
					}
				}
			} else {
				fields = new String[3];
				if (s.length() == 8) {
					fields[0] = s.substring(0, 4);
					fields[1] = s.substring(4, 6);
					fields[2] = s.substring(6, 8);
				} else if (s.length() == 6) {
					fields[0] = s.substring(0, 2);
					fields[1] = s.substring(2, 4);
					fields[2] = s.substring(4, 6);
				}
				year = Integer.parseInt(fields[0]);
				month = Integer.parseInt(fields[1]);
				day = Integer.parseInt(fields[2]);
			}

			if (year < 60) {
				year = year + 2000;
			} else if (year < 100) {
				year = year + 1900;
			}

			if (year == 0 || month == 0 || day == 0) {
				throw new ParseException("Cannot parse " + s, 0);
			}

			String dateStr = month + "/" + day + "/" + year;
			return DF.parse(dateStr);

		} catch (Throwable ex) {
			if (ex instanceof ParseException)
				throw (ParseException) ex;
			throw new ParseException("Cannot parse " + s, 0);
		}
	}

	public static int parseMonth(String mmm) throws ParseException {
		String month = mmm.trim().toUpperCase();
		if (month.startsWith("JAN"))
			return 1;
		if (month.startsWith("FEB"))
			return 2;
		if (month.startsWith("MAR"))
			return 3;
		if (month.startsWith("APR"))
			return 4;
		if (month.startsWith("MAY"))
			return 5;
		if (month.startsWith("JUN"))
			return 6;
		if (month.startsWith("JUL"))
			return 7;
		if (month.startsWith("AUG"))
			return 8;
		if (month.startsWith("SEP"))
			return 9;
		if (month.startsWith("OCT"))
			return 10;
		if (month.startsWith("NOV"))
			return 11;
		if (month.startsWith("DEC"))
			return 12;

		throw new ParseException("Cannot parse month " + mmm, 0);
	}

	public static String formatMonth(int m) {
		switch (m) {
		case 1:
			return "JAN";
		case 2:
			return "FEB";
		case 3:
			return "MAR";
		case 4:
			return "APR";
		case 5:
			return "MAY";
		case 6:
			return "JUN";
		case 7:
			return "JUL";
		case 8:
			return "AUG";
		case 9:
			return "SEP";
		case 10:
			return "OCT";
		case 11:
			return "NOV";
		case 12:
			return "DEC";
		default:
			throw new RuntimeException("Invalid Month " + m);
		}
	}

	public static String parseFutureExpiryMonth(int month, int year)
			throws ParseException {
		char monthCode = getFutureMonthCode(month);
		int singleDigitYear = year % 10;
		return "" + monthCode + singleDigitYear;
	}

	public static char getFutureMonthCode(int month) {
		switch (month) {
		case 1:
			return 'F';
		case 2:
			return 'G';
		case 3:
			return 'H';
		case 4:
			return 'J';
		case 5:
			return 'K';
		case 6:
			return 'M';
		case 7:
			return 'N';
		case 8:
			return 'Q';
		case 9:
			return 'U';
		case 10:
			return 'V';
		case 11:
			return 'X';
		case 12:
			return 'Z';
		default:
			throw new RuntimeException("Invalid Future Month " + month);
		}
	}

	public static char getFutureMonth(char month) {
		switch (month) {
		case 'F':
			return 1;
		case 'G':
			return 2;
		case 'H':
			return 3;
		case 'J':
			return 4;
		case 'K':
			return 5;
		case 'M':
			return 6;
		case 'N':
			return 7;
		case 'Q':
			return 8;
		case 'U':
			return 9;
		case 'V':
			return 10;
		case 'X':
			return 11;
		case 'Z':
			return 12;
		default:
			throw new RuntimeException("Invalid Future Month " + month);
		}
	}

	public static String getGenevaFutureMonthYearCode(Date d) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(d);
		int month = cal.get(Calendar.MONTH) + 1;
		char monthCode = getFutureMonthCode(month);
		int year = cal.get(Calendar.YEAR) - 2000;
		String yearStr = String.valueOf(year);
		if (year < 10)
			yearStr = "0" + yearStr;
		return monthCode + yearStr;
	}

	public static String getGenevaFutureMonthYearBloombergCode(Date d) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(d);
		int month = cal.get(Calendar.MONTH) + 1;
		char monthCode = getFutureMonthCode(month);
		int year = cal.get(Calendar.YEAR) - 2000;
		String yearStr = String.valueOf(year).substring(1);
		if (year < 10)
			yearStr = "0" + yearStr;
		return monthCode + yearStr;
	}

	public static Date getLastBizDay() {
		Calendar cal = Calendar.getInstance();
		while (true) {
			cal.add(Calendar.DATE, -1);
			if (isNoneBizDay(cal))
				continue;
			return cal.getTime();
		}
	}

	public static boolean isIMM(Date d) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(d);

		if (cal.get(Calendar.DAY_OF_WEEK) != Calendar.WEDNESDAY)
			return false;

		int month = cal.get(Calendar.MONTH);
		if (((month + 1) % 3) != 0)
			return false;

		int day = cal.get(Calendar.DATE);

		return day - 14 > 0 && day - 21 <= 0;
	}

	public static boolean isCADIMM(Date d) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(d);
		cal.add(Calendar.DATE, 2);
		return isIMM(cal.getTime());

	}

	public static Date addBizDays(Date d, int i) {
		Calendar c = Calendar.getInstance();
		c.setTime(d);

		int j = 0;

		if (i > 0) {
			while (j < i) {
				c.add(Calendar.DATE, 1);
				if (isNoneBizDay(c)) {

				} else {
					j++;
				}

			}
		}

		if (i < 0) {
			while (j > i) {
				c.add(Calendar.DATE, -1);
				if (isNoneBizDay(c)) {

				} else {
					j--;
				}

			}
		}

		return c.getTime();
	}

	public static boolean isNoneBizDay(Calendar c) {
		int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
		if (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY)
			return true;

		return Holiday.isHoliday(c.getTime());
	}

}
